package com.wiscamp.ninechapters.problems.infrastructure.repositories;

import com.wiscamp.ninechapters.problems.domain.models.sets.KnowledgePointRule;
import com.wiscamp.ninechapters.problems.domain.models.sets.ProblemTypeRule;
import com.wiscamp.ninechapters.problems.domain.models.sets.RegroupRule;
import com.wiscamp.ninechapters.problems.domain.models.sets.TagRule;
import com.wiscamp.ninechapters.problems.domain.repositories.RegroupRuleRepository;
import com.wiscamp.ninechapters.problems.enums.Actions;
import com.wiscamp.ninechapters.problems.infrastructure.converters.RegroupRuleConverter;
import com.wiscamp.ninechapters.problems.infrastructure.mappers.sets.*;
import com.wiscamp.ninechapters.problems.infrastructure.po.sets.*;
import cube.common.core.CollectionUtils;
import cube.ddd.aspect.HandleDataException;
import cube.ddd.common.query.PagedObjects;
import cube.ddd.common.query.PagedQuery;
import cube.ddd.domain.BaseRepository;
import cube.ddd.utils.PersistenceUtils;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import java.util.Collection;
import java.util.Objects;
import java.util.stream.Collectors;

@Repository
public class RegroupRuleRepositoryImpl extends BaseRepository implements RegroupRuleRepository {

    private final RegroupRuleMapper regroupRuleMapper;

    private final RegroupRuleViewMapper regroupRuleViewMapper;

    private final ProblemTypeRuleMapper problemTypeRuleMapper;

    private final TagRuleMapper tagRuleMapper;

    private final KnowledgePointRuleMapper knowledgePointRuleMapper;

    @Autowired
    public RegroupRuleRepositoryImpl(RegroupRuleMapper regroupRuleMapper, RegroupRuleViewMapper regroupRuleViewMapper, ProblemTypeRuleMapper problemTypeRuleMapper, TagRuleMapper tagRuleMapper, KnowledgePointRuleMapper knowledgePointRuleMapper) {
        this.regroupRuleMapper = regroupRuleMapper;
        this.regroupRuleViewMapper = regroupRuleViewMapper;
        this.problemTypeRuleMapper = problemTypeRuleMapper;
        this.tagRuleMapper = tagRuleMapper;
        this.knowledgePointRuleMapper = knowledgePointRuleMapper;
    }

    private RegroupRulePO getRegroupRulePO(@Min(0) long ruleId) {
        return PersistenceUtils.findById(ruleId, regroupRuleMapper::findById);
    }

    private void loadDetails(RegroupRule rule) {
        if (Objects.nonNull(rule)) {
            if (rule.isHasTagRule()) {
                var tagRules = PersistenceUtils.getEntitiesById(rule.getRuleId(), tagRuleMapper::findTagRules,
                        RegroupRuleConverter.Instance::toTagRule);
                rule.setTagRules(tagRules);
            }

            if (rule.isHasKnowledgePointRule()) {
                var pointRules = PersistenceUtils.getEntitiesById(rule.getRuleId(), knowledgePointRuleMapper::findKnowledgePointRules,
                        RegroupRuleConverter.Instance::toKnowledgePointRule);
                rule.setKnowledgePointRules(pointRules);
            }

            if (rule.isHasProblemTypeRule()) {
                var typeRules = PersistenceUtils.getEntitiesById(rule.getRuleId(), problemTypeRuleMapper::findProblemTypeRules,
                        RegroupRuleConverter.Instance::toProblemTypeRule);
                rule.setProblemTypeRules(typeRules);
            }
        }
    }

    /**
     * Gets a regroup rule
     *
     * @param ruleId
     * @return
     */
    @HandleDataException
    public RegroupRule getRegroupRule(@Min(0) long ruleId) {
        var rule = PersistenceUtils.findEntityById(ruleId, regroupRuleViewMapper::findById,
                RegroupRuleConverter.Instance::toRegroupRuleByView);
        loadDetails(rule);
        return rule;
    }

    /**
     * Gets a regroup rule
     *
     * @param ruleCode
     * @param organizationId
     * @return
     */
    @HandleDataException
    public RegroupRule getRegroupRuleByCode(@NotBlank String ruleCode, @Min(0) long organizationId) {
        var rule = PersistenceUtils.getEntityByTextAndId(ruleCode, organizationId, regroupRuleViewMapper::findByRuleCode,
                RegroupRuleConverter.Instance::toRegroupRuleByView);
        loadDetails(rule);
        return rule;
    }

    /**
     * Gets regroup rules
     *
     * @param ruleName
     * @param organizationId
     * @return
     */
    @HandleDataException
    public Collection<RegroupRule> getRegroupRulesByName(@NotBlank String ruleName, @Min(0) long organizationId) {
        var rules = PersistenceUtils.getEntitiesByTextAndId(ruleName, organizationId, regroupRuleViewMapper::findListByRuleName,
                RegroupRuleConverter.Instance::toRegroupRuleByView);
        for (var rule : rules) {
            loadDetails(rule);
        }
        return rules;
    }


    @HandleDataException
    public Collection<RegroupRule> getRegroupRulesByCode(@NotBlank String ruleCode, @Min(0) long organizationId) {
        ruleCode = ruleCode + "%";
        var rules = PersistenceUtils.getEntitiesByTextAndId(ruleCode, organizationId, regroupRuleViewMapper::findListByRuleCode,
                RegroupRuleConverter.Instance::toRegroupRuleByView);
        for (var rule : rules) {
            loadDetails(rule);
        }
        return rules;
    }

    /**
     * Gets regroup rules
     *
     * @param organizationId
     * @return
     */
    @HandleDataException
    public Collection<RegroupRule> getRegroupRulesByOrganization(@Min(0) long organizationId) {
        var rules = PersistenceUtils.getEntitiesById(organizationId, regroupRuleViewMapper::findListByOrganization,
                RegroupRuleConverter.Instance::toRegroupRuleByView);
        for (var rule : rules) {
            loadDetails(rule);
        }
        return rules;
    }

    /**
     * Gets regroup rules
     *
     * @param creatorId
     * @return
     */
    @HandleDataException
    public Collection<RegroupRule> getRulesByCreator(@Min(0) long creatorId) {
        var rules = PersistenceUtils.getEntitiesById(creatorId, regroupRuleViewMapper::findListByCreator,
                RegroupRuleConverter.Instance::toRegroupRuleByView);
        for (var rule : rules) {
            loadDetails(rule);
        }
        return rules;
    }

    /**
     * Gets regroup rules for amc special training 通过amc级别获取专项练习规则列表
     *
     * @param gradeLevel : 一年级 / 二年级 ... / 六年级
     * @return
     */
    @HandleDataException
    public Collection<RegroupRule> getSpecialPracticeRules(@NotBlank String gradeLevel) {
        var namePrefix = "专项练习%";
        var ruleViews = regroupRuleViewMapper.findListBySpecialPractice(namePrefix);
        ruleViews = ruleViews.stream().filter(e -> e.getRuleName().toLowerCase().contains(gradeLevel.toLowerCase()))
                .collect(Collectors.toList());
        var rules = CollectionUtils.toArrayList(ruleViews, RegroupRuleConverter.Instance::toRegroupRuleByView);
        for (var rule : rules) {
            loadDetails(rule);
        }
        return rules;
    }

    /**
     * Gets regroup rules by pager
     *
     * @param pagination
     * @return
     */

    @HandleDataException
    public PagedObjects<RegroupRule> getRegroupRulesByQuery(@NotNull PagedQuery query) {
        PagedObjects<RegroupRuleView> pagedObjects = PersistenceUtils.getPersistenceObjectsByQuery(query, regroupRuleViewMapper::findAll);
        return PersistenceUtils.convertPagedObjects(pagedObjects, RegroupRuleConverter.Instance::toRegroupRuleByView);
    }

    /**
     * Creates a regroup rule
     *
     * @param rule
     * @return
     */
    @HandleDataException
    public RegroupRule saveRegroupRule(@NotNull RegroupRule rule) {
        var newId = rule.getRuleId();
        if (rule.getRuleId() <= 0) {
            var newRule = PersistenceUtils.createEntity(rule, regroupRuleMapper::saveAndFlush,
                    RegroupRuleConverter.Instance::toRegroupRulePO, RegroupRuleConverter.Instance::toRegroupRule);
            newId = newRule.getRuleId();

            // save tagRules
            if (rule.isHasTagRule()) {
                for (var tagRule : rule.getTagRules()) {
                    tagRule.setRuleId(newId);
                    saveTagRule(tagRule);
                }
            }

            // save knowledgePointRules
            if (rule.isHasKnowledgePointRule()) {
                for (var knowledgePointRule : rule.getKnowledgePointRules()) {
                    knowledgePointRule.setRuleId(newId);
                    saveKnowledgePointRule(knowledgePointRule);
                }
            }

            // save problemTypeRules
            if (rule.isHasProblemTypeRule()) {
                for (var problemTypeRule : rule.getProblemTypeRules()) {
                    problemTypeRule.setRuleId(newId);
                    saveProblemTypeRule(problemTypeRule);
                }
            }
        } else {
            var existedPO = getRegroupRulePO(rule.getRuleId());
            if (existedPO == null) existedPO = new RegroupRulePO();
            PersistenceUtils.updateEntity(rule, existedPO, regroupRuleMapper::saveAndFlush,
                    RegroupRuleConverter.Instance::toRegroupRulePO, RegroupRuleConverter.Instance::toRegroupRule);

            // save tagRules
            if (rule.isHasTagRule()) {
                for (var tagRule : rule.getTagRules()) {
                    if (tagRule.getAction() == Actions.DELETE)
                        deleteTagRule(tagRule.getId());
                    else {
                        tagRule.setRuleId(newId);
                        saveTagRule(tagRule);
                    }
                }
            }
            // save knowledgePointRules
            if (rule.isHasKnowledgePointRule()) {
                for (var knowledgePointRule : rule.getKnowledgePointRules()) {
                    if (knowledgePointRule.getAction() == Actions.DELETE)
                        deleteKnowledgePointRule(knowledgePointRule.getId());
                    else {
                        knowledgePointRule.setRuleId(newId);
                        saveKnowledgePointRule(knowledgePointRule);
                    }
                }
            }

            // save problemTypeRules
            if (rule.isHasProblemTypeRule()) {
                for (var problemTypeRule : rule.getProblemTypeRules()) {
                    if (problemTypeRule.getAction() == Actions.DELETE)
                        deleteProblemTypeRule(problemTypeRule.getId());
                    else {
                        problemTypeRule.setRuleId(newId);
                        saveProblemTypeRule(problemTypeRule);
                    }
                }
            }
        }

        return getRegroupRule(newId);
    }


    // region tagRule
    private TagRulePO getTagRulePO(long id) {
        return PersistenceUtils.findById(id, tagRuleMapper::findById);
    }

    private TagRule getTagRule(long id) {
        return PersistenceUtils.findEntityById(id, tagRuleMapper::findById, RegroupRuleConverter.Instance::toTagRule);
    }

    private TagRule saveTagRule(@NotNull TagRule rule) {
        var newId = rule.getRuleId();
        if (rule.getRuleId() <= 0) {
            var newRule = PersistenceUtils.createEntity(rule, tagRuleMapper::saveAndFlush,
                    RegroupRuleConverter.Instance::toTagRulePO, RegroupRuleConverter.Instance::toTagRule);
            newId = newRule.getRuleId();

        } else {
            var existedPO = getTagRulePO(rule.getRuleId());
            if (existedPO == null) existedPO = new TagRulePO();
            PersistenceUtils.updateEntity(rule, existedPO, tagRuleMapper::saveAndFlush,
                    RegroupRuleConverter.Instance::toTagRulePO, RegroupRuleConverter.Instance::toTagRule);
        }

        return getTagRule(newId);
    }

    private boolean deleteTagRule(long id) {
        tagRuleMapper.deleteById(id);
        return true;
    }
    // endregion


    // region problemTypeRule
    private ProblemTypeRulePO getProblemTypeRulePO(long id) {
        return PersistenceUtils.findById(id, problemTypeRuleMapper::findById);
    }

    private ProblemTypeRule getProblemTypeRule(long id) {
        return PersistenceUtils.findEntityById(id, problemTypeRuleMapper::findById, RegroupRuleConverter.Instance::toProblemTypeRule);
    }

    private ProblemTypeRule saveProblemTypeRule(@NotNull ProblemTypeRule rule) {
        var newId = rule.getRuleId();
        if (rule.getRuleId() <= 0) {
            var newRule = PersistenceUtils.createEntity(rule, problemTypeRuleMapper::saveAndFlush,
                    RegroupRuleConverter.Instance::toProblemTypeRulePO, RegroupRuleConverter.Instance::toProblemTypeRule);
            newId = newRule.getRuleId();

        } else {
            var existedPO = getProblemTypeRulePO(rule.getRuleId());
            if (existedPO == null) existedPO = new ProblemTypeRulePO();
            PersistenceUtils.updateEntity(rule, existedPO, problemTypeRuleMapper::saveAndFlush,
                    RegroupRuleConverter.Instance::toProblemTypeRulePO, RegroupRuleConverter.Instance::toProblemTypeRule);
        }

        return getProblemTypeRule(newId);
    }

    private boolean deleteProblemTypeRule(long id) {
        problemTypeRuleMapper.deleteById(id);
        return true;
    }
    // endregion


    // region knowledgePointRule
    private KnowledgePointRulePO getKnowledgePointRulePO(long id) {
        return PersistenceUtils.findById(id, knowledgePointRuleMapper::findById);
    }

    private KnowledgePointRule getKnowledgePointRule(long id) {
        return PersistenceUtils.findEntityById(id, knowledgePointRuleMapper::findById, RegroupRuleConverter.Instance::toKnowledgePointRule);
    }

    private KnowledgePointRule saveKnowledgePointRule(@NotNull KnowledgePointRule rule) {
        var newId = rule.getRuleId();
        if (rule.getRuleId() <= 0) {
            var newRule = PersistenceUtils.createEntity(rule, knowledgePointRuleMapper::saveAndFlush,
                    RegroupRuleConverter.Instance::toKnowledgePointRulePO, RegroupRuleConverter.Instance::toKnowledgePointRule);
            newId = newRule.getRuleId();

        } else {
            var existedPO = getKnowledgePointRulePO(rule.getRuleId());
            if (existedPO == null) existedPO = new KnowledgePointRulePO();
            PersistenceUtils.updateEntity(rule, existedPO, knowledgePointRuleMapper::saveAndFlush,
                    RegroupRuleConverter.Instance::toKnowledgePointRulePO, RegroupRuleConverter.Instance::toKnowledgePointRule);
        }

        return getKnowledgePointRule(newId);
    }

    private boolean deleteKnowledgePointRule(long id) {
        knowledgePointRuleMapper.deleteById(id);
        return true;
    }
    // endregion
}
